Data
Let’s import the data we’ll be using. The data is from the Stanford Open Policing Project and includes vehicle stops by the Evanston police in 2017. We’re reading the data in from a URL directly.
We’re going to use the read_csv function from the readr package, which is part of the tidyverse. The read_csv function works like read.csv except is has some different defaults, guesses data types a bit differently, and produces a tibble instead of a normal data frame (details coming).
police <- read_csv("https://github.com/nuitrcs/r-online-2020/raw/master/data/ev_police.csv")
##
## ── Column specification ────────────────────────────────────────────────────────
## cols(
## .default = col_character(),
## raw_row_number = col_double(),
## date = col_date(format = ""),
## time = col_time(format = ""),
## location = col_double(),
## beat = col_double(),
## subject_age = col_logical(),
## department_id = col_double(),
## citation_issued = col_logical(),
## warning_issued = col_logical(),
## contraband_found = col_logical(),
## contraband_drugs = col_logical(),
## contraband_weapons = col_logical(),
## search_conducted = col_logical(),
## search_person = col_logical(),
## search_vehicle = col_logical(),
## vehicle_year = col_double(),
## raw_DriverRace = col_double(),
## raw_ReasonForStop = col_double(),
## raw_TypeOfMovingViolation = col_double(),
## raw_ResultOfStop = col_double()
## )
## ℹ Use `spec()` for the full column specifications.
## Warning: 7 parsing failures.
## row col expected actual file
## 1015 beat no trailing characters 78Q 'https://github.com/nuitrcs/r-online-2020/raw/master/data/ev_police.csv'
## 1040 beat a double / 'https://github.com/nuitrcs/r-online-2020/raw/master/data/ev_police.csv'
## 1295 beat no trailing characters 71` 'https://github.com/nuitrcs/r-online-2020/raw/master/data/ev_police.csv'
## 1891 beat a double CHICAGO 'https://github.com/nuitrcs/r-online-2020/raw/master/data/ev_police.csv'
## 4335 beat no trailing characters 71? 'https://github.com/nuitrcs/r-online-2020/raw/master/data/ev_police.csv'
## .... .... ...................... ....... ........................................................................
## See problems(...) for more details.
The output message that you get tells you want data type it guessed for each column based on the format of the information. col_double() means numeric data with decimal values (not just integers). col_logical() means boolean TRUE/FALSE values. Note that it also automatically read and identified date and time values and converted them to date and time objects – not just string/character data.
At the bottom of the output, it also tells you about any problems it encountered: when if found a value that doesn’t match what it thinks is the data type for the column. When it guesses the data type for each column, it only uses the first 1000 rows by default.
police <- read_csv("https://github.com/nuitrcs/r-online-2020/raw/master/data/ev_police.csv",
col_types=c("beat"="c"))
The col_types argument functions like colClasses for read.csv – it allows us to say which type of data is in a particular column instead of having the function just guess and determine the type for us. Here we’re saying that the column named “beat” has “c”haracter type data in it. By default, the function will think it’s numeric data, because most of the values are numbers, including all of the first 1000 rows that the function uses by default to guess what type of data is in each column.
One of the big other differences, which was relevant before R version 4.0 came out this year, is that read_csv does not import text data as factors, while read.csv had stringAsFactors=FALSE by default prior to R 4.0.
Tibbles
Tibbles are the tidyverse version of a data frame. You can use them as you would a data frame (they are one), but they behave in slightly different ways.
police
The most observable difference is that tibbles will only print 10 rows and the columns that will fit in your console. When they print, they print a list of column names and the types of the columns that are shown.
To view the dataset, use View():
View(police)
When using [] notation to subset them, they will always return a tibble. In constrast, data frames sometimes return a data frame and sometimes return just a vector.
police[, 1]
as.data.frame(police)[, 1]
Tibbles are also stricter about name matching, and they essentially ignore row names.
dplyr
dplyr is at the core of the tidyverse. It is for working with data frames. It contains six main functions, each a verb, of actions you frequently take with a data frame. We’re covering 3 of those functions today (select, filter, mutate), and 3 more next week (group_by, summarize, arrange).
Each of these function takes a data frame as the first input. Within the function call, we can refer to the column names without quotes and without $ notation.
Select: Choose Columns
The select function is for selecting which columns to keep (or exclude) from the data set. First, let’s remember what the column names are:
names(police)
Now, let’s select just “date” and “outcome” from police tibble (data frame):
select(police, date, outcome)
The first input is the name of a data frame. Then we can list one or more columns that we want to select after that, comma separated. Notice there are no quotes around the column names, and we don’t need to preface the names with anything (similar to what we do with the formula syntax in base R).
We can name all of the columns we want, like above. We can also say which columns we don’t want by putting a - in front of the name:
select(police, -raw_row_number, -subject_age)
When using negated - column names, it will include all other columns by default.
As with [] indexing, columns will be returned in the order specified:
select(police, subject_sex, subject_race, date)
We could also use the column index number if we wanted to instead. We don’t need to put the values in c() like we would with [] (but we could).
select(police, 1, 4, 10)
There are a number of select helper functions and special syntax options that allow us to choose what columns we want to keep.
First, we can use : for range, but with names in addition to numbers:
select(police, raw_DriverRace:raw_ResultOfStop)
We can select the rightmost columns with last_col():
select(police, last_col())
Last 4 columns (the input to the function is the offset # of columns from the right edge):
select(police, last_col(0:3))
We can also select by matching patterns in the names of the columns:
select(police, starts_with("raw"))
select(police, ends_with("issued"))
select(police, contains("vehicle"))
We can also put a - in front of these helper functions to exclude columns:
select(police, -contains("subject"))
And there are even more helper functions.
EXERCISE
Use select() to get a copy of police without the columns that start with “raw”.
Hint: If you mess up your police dataset, re-run the cell near the top of the file under the Data header and read the data in again fresh.
Renaming
We can also rename columns while using select(). The syntax is new_name = old_name.
select(police, raw_id=raw_row_number, date, time)
or we can use rename() to only rename, without affecting which columns are included:
rename(police, raw_id=raw_row_number)
This doesn’t change police because we didn’t save the result. So far, we’ve just been printing the copy of the data frame that is returned by the function. If we want to change our data frame, we’d need to save the result back to the police object.
police <- rename(police, raw_id=raw_row_number)
Filter: Choose Rows
The filter() function lets us choose which rows of data to keep. Takes the data frame name first, and then any expression that will return a vector of TRUE and FALSE values that is the same length as the number of rows in the data frame.
filter(police, date == "2017-01-02")
We can do complex conditions as we could do with []
filter(police, subject_race == "hispanic" & subject_sex == "female")
If we include multiple comma-separated conditions, they are joined with & and. So this following is equivalent to the above.
filter(police, subject_race == "hispanic", subject_sex == "female")
EXERCISE
- Filter
police to choose the rows where location is 60201 or 60202
- Filter
police to choose the rows where location is 60201 or 60202 and subject_sex is “male”
Hints:
- The “or” operator is
|; the “and” operator is &
Pipe: Chaining Commands Together
So, we can choose rows and choose columns separately; how do we combine these operations? dplyr, and other tidyverse, commands can be strung together is a series with a %>% (say/read: pipe) operator. If you are familiar with working in a terminal/at the command line, it works like a bash pipe character |. It takes the output of the command on the left and makes that the first input to the command on the right.
This works because the functions all take a data frame as the first input, and they return a data frame as the output.
We can rewrite
select(police, date, time)
as
police %>% select(date, time)
and you’ll often see code formatted, so %>% is at the end of each line:
police %>%
select(date, time)
The pipe comes from a package called magrittr, which has additional special operators in it that you can use. The keyboard shortcut for %>% is command-shift-M (Mac) or control-shift-M (Windows).
We can use the pipe to string together multiple commands operating on the same data frame:
police %>%
select(subject_race, subject_sex) %>%
filter(subject_race == "white")
We would read the %>% in the command above as “then” if reading the code outloud: from police, select subject_race and subject_sex, then filter where subject_race is white.
Order does matter, as the commands are executed in order. So this would give us an error:
police %>%
select(subject_sex, outcome) %>%
filter(subject_race == "white")
Because subject_race is no longer in the data frame once we try to filter on it. We’d have to reverse the order:
police %>%
filter(subject_race == "white") %>%
select(subject_sex, outcome)
You can use the pipe operator to string together commands outside of the tidyverse as well:
# sort(table(police$subject_race)) becomes:
table(police$subject_race) %>% sort()
EXERCISE
Select the date, time, and outcome (columns) of stops that occur in beat 71 (rows). Hint: remember that a column needs to still be in the data frame if you’re going to use the column to filter.
Mutate: Change or Create Columns
mutate() is used to both change the values of an existing column and make a new column.
mutate(police, vehicle_age = 2017 - vehicle_year) %>%
select(starts_with("vehicle"))
Within a call to mutate, we can refer to variables we made or changed earlier in the same call as well. Here, we create vehicle_age, and then use it to create vehicle_age_norm:
police %>%
mutate(vehicle_age = 2017 - vehicle_year,
vehicle_age_norm = ifelse(vehicle_age < 0, 0, vehicle_age)) %>%
select(starts_with("vehicle")) %>%
filter(vehicle_age < 0)
Side note: there is a tidyverse version of ifelse() called if_else() that works generally the same except it is stricter about checking data types.
mutate() can also change an existing column. The location column in the data contains zip codes, that were read in as numeric values. This means the leading zero on some zip codes has been lost. Convert location to character data, and add back in the leading 0 if it should be there.
Here I’ll change the location column twice in the same call with two different transformations:
police %>%
mutate(location = as.character(location), # first convert to character, then recode below
location = ifelse(nchar(location) == 4, # ifelse test (vector of TRUE and FALSE)
paste0("0", location), # value if TRUE
location)) %>% # value if FALSE
select(location) %>% # selecting just the column we mutated to look at
filter(startsWith(location, "0")) # selecting a few rows to look at the change
Remember that when using mutate(), you’re operating on the entire column at once, so you can’t select just a subset of the vector as you would with []. This means more frequently using functions like ifelse() or helper functions such as na_if(), replace_na(), or recode().
mutate(police, vehicle_make = na_if(vehicle_make, "UNK"))
EXERCISE
If beat is “/” or “CHICAGO”, set it to NA instead using mutate().
Hint: if you use na_if(), it only can check and replace one value at a time, so you’d need to use it twice (which is OK!). With ifelse() you can write an expression like (beat == '/' | beat == 'CHICAGO') or (beat %in% c('/', 'CHICAGO')) for the first input (the TRUE/FALSE test).
LS0tCnRpdGxlOiAiVGlkeXZlcnNlIGJhc2ljcyIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKICAgIGNvZGVfZG93bmxvYWQ6IFRSVUUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAyCmVkaXRvcl9vcHRpb25zOgogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUKLS0tCgpgYGB7ciwgc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiMgeW91IGRvbid0IG5lZWQgdG8gcnVuIHRoaXMgd2hlbiB3b3JraW5nIGluIFJTdHVkaW8Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGV2YWw9RkFMU0UpICAjIHdoZW4gbWFraW5nIHRoZSBodG1sIHZlcnNpb24gb2YgdGhpcyBmaWxlLCBkb24ndCBleGVjdXRlIHRoZSBjb2RlCmBgYAoKKlRoZSBvdXRwdXQgb2YgbW9zdCBvZiB0aGUgUiBjaHVua3MgaXNuJ3QgaW5jbHVkZWQgaW4gdGhlIEhUTUwgdmVyc2lvbiBvZiB0aGUgZmlsZSB0byBrZWVwIGl0IHRvIGEgbW9yZSByZWFzb25hYmxlIGZpbGUgc2l6ZS4gIFlvdSBjYW4gcnVuIHRoZSBjb2RlIGluIFIgdG8gc2VlIHRoZSBvdXRwdXQuKgoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cHM6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vKSBkb2N1bWVudC4gIEZvbGxvdyB0aGUgbGluayB0byBsZWFybiBtb3JlIGFib3V0IFIgTWFya2Rvd24gYW5kIHRoZSBub3RlYm9vayBmb3JtYXQgdXNlZCBkdXJpbmcgdGhlIHdvcmtzaG9wLgoKIyBTZXR1cAoKYGBge3IsIGV2YWw9VFJVRX0KbGlicmFyeSh0aWR5dmVyc2UpCmBgYAoKVGhpcyBnaXZlcyB5b3UgaW5mbyBvbiB3aGljaCBwYWNrYWdlcyBpdCBhY3R1YWxseSBsb2FkZWQsIGJlY2F1c2Ugd2hlbiB5b3UgaW5zdGFsbCB0aWR5dmVyc2UsIGl0IGluc3RhbGxzIH4yNSBwYWNrYWdlcywgYnV0IGl0IG9ubHkgbG9hZHMgdGhlIG9uZXMgbGlzdGVkLiAgVGlkeXZlcnNlIHBhY2thZ2VzIGFsc28gdGVuZCB0byBiZSB2ZXJib3NlIGluIHdhcm5pbmcgeW91IHdoZW4gdGhlcmUgYXJlIGZ1bmN0aW9ucyB3aXRoIHRoZSBzYW1lIG5hbWUgaW4gbXVsdGlwbGUgcGFja2FnZXMuCgojIEJhY2tncm91bmQKClRpZHl2ZXJzZSBwYWNrYWdlcyBkbyBhIGZldyB0aGluZ3M6CgoqIGZpeCBzb21lIG9mIHRoZSBhbm5veWluZyBwYXJ0cyBvZiB1c2luZyBSLCBzdWNoIGFzIGNoYW5naW5nIGRlZmF1bHQgb3B0aW9ucyB3aGVuIGltcG9ydGluZyBkYXRhIGZpbGVzIGFuZCBwcmV2ZW50aW5nIGxhcmdlIGRhdGEgZnJhbWVzIGZyb20gcHJpbnRpbmcgdG8gdGhlIGNvbnNvbGUKKiBhcmUgZm9jdXNlZCBvbiB3b3JraW5nIHdpdGggZGF0YSBmcmFtZXMgKGFuZCB0aGVpciBjb2x1bW5zKSwgcmF0aGVyIHRoYW4gaW5kaXZpZHVhbCB2ZWN0b3JzCiogdXN1YWxseSB0YWtlIGEgZGF0YSBmcmFtZSBhcyB0aGUgZmlyc3QgaW5wdXQgdG8gYSBmdW5jdGlvbiwgYW5kIHJldHVybiBhIGRhdGEgZnJhbWUgYXMgdGhlIG91dHB1dCBvZiBhIGZ1bmN0aW9uLCBzbyB0aGF0IGZ1bmN0aW9uIGNhbGxzIGNhbiBiZSBtb3JlIGVhc2lseSBzdHJ1bmcgdG9nZXRoZXIgaW4gYSBzZXF1ZW5jZQoqIHNoYXJlIHNvbWUgY29tbW9uIG5hbWluZyBjb252ZW50aW9ucyBmb3IgZnVuY3Rpb25zIGFuZCBhcmd1bWVudHMgdGhhdCBoYXZlIGEgZ29hbCBvZiBtYWtpbmcgY29kZSBtb3JlIHJlYWRhYmxlCiogdGVuZCB0byBiZSB2ZXJib3NlLCBvcGluaW9uYXRlZCwgYW5kIGFyZSBhY3RpdmVseSB3b3JraW5nIHRvIHByb3ZpZGUgbW9yZSB1c2VmdWwgZXJyb3IgbWVzc2FnZXMKClRpZHl2ZXJzZSBwYWNrYWdlcyBhcmUgcGFydGljdWxhcmx5IHVzZWZ1bCBmb3I6CgoqIGRhdGEgZXhwbG9yYXRpb24KKiByZXNoYXBpbmcgZGF0YSBzZXRzCiogY29tcHV0aW5nIHN1bW1hcnkgbWVhc3VyZXMgb3ZlciBncm91cHMKKiBjbGVhbmluZyB1cCBkaWZmZXJlbnQgdHlwZXMgb2YgZGF0YQoqIHJlYWRpbmcgYW5kIHdyaXRpbmcgZGF0YQoKIyBEYXRhCgpMZXQncyBpbXBvcnQgdGhlIGRhdGEgd2UnbGwgYmUgdXNpbmcuICBUaGUgZGF0YSBpcyBmcm9tIHRoZSBbU3RhbmZvcmQgT3BlbiBQb2xpY2luZyBQcm9qZWN0XShodHRwczovL29wZW5wb2xpY2luZy5zdGFuZm9yZC5lZHUvZGF0YS8pIGFuZCBpbmNsdWRlcyB2ZWhpY2xlIHN0b3BzIGJ5IHRoZSBFdmFuc3RvbiBwb2xpY2UgaW4gMjAxNy4gIFdlJ3JlIHJlYWRpbmcgdGhlIGRhdGEgaW4gZnJvbSBhIFVSTCBkaXJlY3RseS4gIAoKV2UncmUgZ29pbmcgdG8gdXNlIHRoZSBgcmVhZF9jc3ZgIGZ1bmN0aW9uIGZyb20gdGhlIGByZWFkcmAgcGFja2FnZSwgd2hpY2ggaXMgcGFydCBvZiB0aGUgdGlkeXZlcnNlLiAgVGhlIGByZWFkX2NzdmAgZnVuY3Rpb24gd29ya3MgbGlrZSBgcmVhZC5jc3ZgIGV4Y2VwdCBpcyBoYXMgc29tZSBkaWZmZXJlbnQgZGVmYXVsdHMsIGd1ZXNzZXMgZGF0YSB0eXBlcyBhIGJpdCBkaWZmZXJlbnRseSwgYW5kIHByb2R1Y2VzIGEgdGliYmxlIGluc3RlYWQgb2YgYSBub3JtYWwgZGF0YSBmcmFtZSAoZGV0YWlscyBjb21pbmcpLiAgCgpgYGB7ciwgZXZhbD1UUlVFfQpwb2xpY2UgPC0gcmVhZF9jc3YoImh0dHBzOi8vZ2l0aHViLmNvbS9udWl0cmNzL3Itb25saW5lLTIwMjAvcmF3L21hc3Rlci9kYXRhL2V2X3BvbGljZS5jc3YiKQpgYGAKClRoZSBvdXRwdXQgbWVzc2FnZSB0aGF0IHlvdSBnZXQgdGVsbHMgeW91IHdhbnQgZGF0YSB0eXBlIGl0IGd1ZXNzZWQgZm9yIGVhY2ggY29sdW1uIGJhc2VkIG9uIHRoZSBmb3JtYXQgb2YgdGhlIGluZm9ybWF0aW9uLiAgYGNvbF9kb3VibGUoKWAgbWVhbnMgbnVtZXJpYyBkYXRhIHdpdGggZGVjaW1hbCB2YWx1ZXMgKG5vdCBqdXN0IGludGVnZXJzKS4gIGBjb2xfbG9naWNhbCgpYCBtZWFucyBib29sZWFuIFRSVUUvRkFMU0UgdmFsdWVzLiAgTm90ZSB0aGF0IGl0IGFsc28gYXV0b21hdGljYWxseSByZWFkIGFuZCBpZGVudGlmaWVkIGRhdGUgYW5kIHRpbWUgdmFsdWVzIGFuZCBjb252ZXJ0ZWQgdGhlbSB0byBkYXRlIGFuZCB0aW1lIG9iamVjdHMgLS0gbm90IGp1c3Qgc3RyaW5nL2NoYXJhY3RlciBkYXRhLiAgICAKCkF0IHRoZSBib3R0b20gb2YgdGhlIG91dHB1dCwgaXQgYWxzbyB0ZWxscyB5b3UgYWJvdXQgYW55IHByb2JsZW1zIGl0IGVuY291bnRlcmVkOiB3aGVuIGlmIGZvdW5kIGEgdmFsdWUgdGhhdCBkb2Vzbid0IG1hdGNoIHdoYXQgaXQgdGhpbmtzIGlzIHRoZSBkYXRhIHR5cGUgZm9yIHRoZSBjb2x1bW4uICBXaGVuIGl0IGd1ZXNzZXMgdGhlIGRhdGEgdHlwZSBmb3IgZWFjaCBjb2x1bW4sIGl0IG9ubHkgdXNlcyB0aGUgZmlyc3QgMTAwMCByb3dzIGJ5IGRlZmF1bHQuIAoKCmBgYHtyLCBldmFsPVRSVUV9CnBvbGljZSA8LSByZWFkX2NzdigiaHR0cHM6Ly9naXRodWIuY29tL251aXRyY3Mvci1vbmxpbmUtMjAyMC9yYXcvbWFzdGVyL2RhdGEvZXZfcG9saWNlLmNzdiIsCiAgICAgICAgICAgICAgICAgICBjb2xfdHlwZXM9YygiYmVhdCI9ImMiKSkKYGBgCgoKClRoZSBgY29sX3R5cGVzYCBhcmd1bWVudCBmdW5jdGlvbnMgbGlrZSBjb2xDbGFzc2VzIGZvciBgcmVhZC5jc3ZgIC0tIGl0IGFsbG93cyB1cyB0byBzYXkgd2hpY2ggdHlwZSBvZiBkYXRhIGlzIGluIGEgcGFydGljdWxhciBjb2x1bW4gaW5zdGVhZCBvZiBoYXZpbmcgdGhlIGZ1bmN0aW9uIGp1c3QgZ3Vlc3MgYW5kIGRldGVybWluZSB0aGUgdHlwZSBmb3IgdXMuICBIZXJlIHdlJ3JlIHNheWluZyB0aGF0IHRoZSBjb2x1bW4gbmFtZWQgImJlYXQiIGhhcyAiYyJoYXJhY3RlciB0eXBlIGRhdGEgaW4gaXQuICBCeSBkZWZhdWx0LCB0aGUgZnVuY3Rpb24gd2lsbCB0aGluayBpdCdzIG51bWVyaWMgZGF0YSwgYmVjYXVzZSBtb3N0IG9mIHRoZSB2YWx1ZXMgYXJlIG51bWJlcnMsIGluY2x1ZGluZyBhbGwgb2YgdGhlIGZpcnN0IDEwMDAgcm93cyB0aGF0IHRoZSBmdW5jdGlvbiB1c2VzIGJ5IGRlZmF1bHQgdG8gZ3Vlc3Mgd2hhdCB0eXBlIG9mIGRhdGEgaXMgaW4gZWFjaCBjb2x1bW4uICAKCk9uZSBvZiB0aGUgYmlnIG90aGVyIGRpZmZlcmVuY2VzLCB3aGljaCB3YXMgcmVsZXZhbnQgYmVmb3JlIFIgdmVyc2lvbiA0LjAgY2FtZSBvdXQgdGhpcyB5ZWFyLCBpcyB0aGF0IGByZWFkX2NzdmAgZG9lcyBub3QgaW1wb3J0IHRleHQgZGF0YSBhcyBmYWN0b3JzLCB3aGlsZSBgcmVhZC5jc3ZgIGhhZCBgc3RyaW5nQXNGYWN0b3JzPUZBTFNFYCBieSBkZWZhdWx0IHByaW9yIHRvIFIgNC4wLiAgCgojIFRpYmJsZXMKClRpYmJsZXMgYXJlIHRoZSB0aWR5dmVyc2UgdmVyc2lvbiBvZiBhIGRhdGEgZnJhbWUuICBZb3UgY2FuIHVzZSB0aGVtIGFzIHlvdSB3b3VsZCBhIGRhdGEgZnJhbWUgKHRoZXkgYXJlIG9uZSksIGJ1dCB0aGV5IGJlaGF2ZSBpbiBzbGlnaHRseSBkaWZmZXJlbnQgd2F5cy4KCmBgYHtyLCBldmFsPVRSVUV9CnBvbGljZQpgYGAKClRoZSBtb3N0IG9ic2VydmFibGUgZGlmZmVyZW5jZSBpcyB0aGF0IHRpYmJsZXMgd2lsbCBvbmx5IHByaW50IDEwIHJvd3MgYW5kIHRoZSBjb2x1bW5zIHRoYXQgd2lsbCBmaXQgaW4geW91ciBjb25zb2xlLiAgV2hlbiB0aGV5IHByaW50LCB0aGV5IHByaW50IGEgbGlzdCBvZiBjb2x1bW4gbmFtZXMgYW5kIHRoZSB0eXBlcyBvZiB0aGUgY29sdW1ucyB0aGF0IGFyZSBzaG93bi4gIAoKVG8gdmlldyB0aGUgZGF0YXNldCwgdXNlIGBWaWV3KClgOgoKYGBge3J9ClZpZXcocG9saWNlKQpgYGAKCldoZW4gdXNpbmcgW10gbm90YXRpb24gdG8gc3Vic2V0IHRoZW0sIHRoZXkgd2lsbCBhbHdheXMgcmV0dXJuIGEgdGliYmxlLiAgSW4gY29uc3RyYXN0LCBkYXRhIGZyYW1lcyBzb21ldGltZXMgcmV0dXJuIGEgZGF0YSBmcmFtZSBhbmQgc29tZXRpbWVzIHJldHVybiBqdXN0IGEgdmVjdG9yLgoKYGBge3J9CnBvbGljZVssIDFdCmFzLmRhdGEuZnJhbWUocG9saWNlKVssIDFdCmBgYAoKVGliYmxlcyBhcmUgYWxzbyBzdHJpY3RlciBhYm91dCBuYW1lIG1hdGNoaW5nLCAgYW5kIHRoZXkgZXNzZW50aWFsbHkgaWdub3JlIHJvdyBuYW1lcy4gIAoKCiMgZHBseXIKCmRwbHlyIGlzIGF0IHRoZSBjb3JlIG9mIHRoZSB0aWR5dmVyc2UuICBJdCBpcyBmb3Igd29ya2luZyB3aXRoIGRhdGEgZnJhbWVzLiAgSXQgY29udGFpbnMgc2l4IG1haW4gZnVuY3Rpb25zLCBlYWNoIGEgdmVyYiwgb2YgYWN0aW9ucyB5b3UgZnJlcXVlbnRseSB0YWtlIHdpdGggYSBkYXRhIGZyYW1lLiAgV2UncmUgY292ZXJpbmcgMyBvZiB0aG9zZSBmdW5jdGlvbnMgdG9kYXkgKHNlbGVjdCwgZmlsdGVyLCBtdXRhdGUpLCBhbmQgMyBtb3JlIG5leHQgd2VlayAoZ3JvdXBfYnksIHN1bW1hcml6ZSwgYXJyYW5nZSkuCgpFYWNoIG9mIHRoZXNlIGZ1bmN0aW9uIHRha2VzIGEgZGF0YSBmcmFtZSBhcyB0aGUgZmlyc3QgaW5wdXQuICBXaXRoaW4gdGhlIGZ1bmN0aW9uIGNhbGwsIHdlIGNhbiByZWZlciB0byB0aGUgY29sdW1uIG5hbWVzIHdpdGhvdXQgcXVvdGVzIGFuZCB3aXRob3V0ICQgbm90YXRpb24uCgojIyBTZWxlY3Q6IENob29zZSBDb2x1bW5zCgpUaGUgYHNlbGVjdGAgZnVuY3Rpb24gaXMgZm9yIHNlbGVjdGluZyB3aGljaCBjb2x1bW5zIHRvIGtlZXAgKG9yIGV4Y2x1ZGUpIGZyb20gdGhlIGRhdGEgc2V0LiAgRmlyc3QsIGxldCdzIHJlbWVtYmVyIHdoYXQgdGhlIGNvbHVtbiBuYW1lcyBhcmU6CgpgYGB7cn0KbmFtZXMocG9saWNlKQpgYGAKCk5vdywgbGV0J3Mgc2VsZWN0IGp1c3QgImRhdGUiIGFuZCAib3V0Y29tZSIgZnJvbSBgcG9saWNlYCB0aWJibGUgKGRhdGEgZnJhbWUpOgoKYGBge3J9CnNlbGVjdChwb2xpY2UsIGRhdGUsIG91dGNvbWUpCmBgYAoKVGhlIGZpcnN0IGlucHV0IGlzIHRoZSBuYW1lIG9mIGEgZGF0YSBmcmFtZS4gIFRoZW4gd2UgY2FuIGxpc3Qgb25lIG9yIG1vcmUgY29sdW1ucyB0aGF0IHdlIHdhbnQgdG8gc2VsZWN0IGFmdGVyIHRoYXQsIGNvbW1hIHNlcGFyYXRlZC4gIE5vdGljZSB0aGVyZSBhcmUgbm8gcXVvdGVzIGFyb3VuZCB0aGUgY29sdW1uIG5hbWVzLCBhbmQgd2UgZG9uJ3QgbmVlZCB0byBwcmVmYWNlIHRoZSBuYW1lcyB3aXRoIGFueXRoaW5nIChzaW1pbGFyIHRvIHdoYXQgd2UgZG8gd2l0aCB0aGUgZm9ybXVsYSBzeW50YXggaW4gYmFzZSBSKS4gIAoKV2UgY2FuIG5hbWUgYWxsIG9mIHRoZSBjb2x1bW5zIHdlIHdhbnQsIGxpa2UgYWJvdmUuICBXZSBjYW4gYWxzbyBzYXkgd2hpY2ggY29sdW1ucyB3ZSBkb24ndCB3YW50IGJ5IHB1dHRpbmcgYSBgLWAgaW4gZnJvbnQgb2YgdGhlIG5hbWU6CgpgYGB7cn0Kc2VsZWN0KHBvbGljZSwgLXJhd19yb3dfbnVtYmVyLCAtc3ViamVjdF9hZ2UpCmBgYAoKV2hlbiB1c2luZyBuZWdhdGVkIGAtYCBjb2x1bW4gbmFtZXMsIGl0IHdpbGwgaW5jbHVkZSBhbGwgb3RoZXIgY29sdW1ucyBieSBkZWZhdWx0LgoKQXMgd2l0aCBgW11gIGluZGV4aW5nLCBjb2x1bW5zIHdpbGwgYmUgcmV0dXJuZWQgaW4gdGhlIG9yZGVyIHNwZWNpZmllZDoKCmBgYHtyfQpzZWxlY3QocG9saWNlLCBzdWJqZWN0X3NleCwgc3ViamVjdF9yYWNlLCBkYXRlKQpgYGAKCldlIGNvdWxkIGFsc28gdXNlIHRoZSBjb2x1bW4gaW5kZXggbnVtYmVyIGlmIHdlIHdhbnRlZCB0byBpbnN0ZWFkLiAgV2UgZG9uJ3QgbmVlZCB0byBwdXQgdGhlIHZhbHVlcyBpbiBgYygpYCBsaWtlIHdlIHdvdWxkIHdpdGggW10gKGJ1dCB3ZSBjb3VsZCkuCgpgYGB7cn0Kc2VsZWN0KHBvbGljZSwgMSwgNCwgMTApCmBgYAoKVGhlcmUgYXJlIGEgbnVtYmVyIG9mIHNlbGVjdCBoZWxwZXIgZnVuY3Rpb25zIGFuZCBzcGVjaWFsIHN5bnRheCBvcHRpb25zIHRoYXQgYWxsb3cgdXMgdG8gY2hvb3NlIHdoYXQgY29sdW1ucyB3ZSB3YW50IHRvIGtlZXAuCgpGaXJzdCwgd2UgY2FuIHVzZSA6IGZvciByYW5nZSwgYnV0IHdpdGggbmFtZXMgaW4gYWRkaXRpb24gdG8gbnVtYmVyczoKCmBgYHtyfQpzZWxlY3QocG9saWNlLCByYXdfRHJpdmVyUmFjZTpyYXdfUmVzdWx0T2ZTdG9wKQpgYGAKCldlIGNhbiBzZWxlY3QgdGhlIHJpZ2h0bW9zdCBjb2x1bW5zIHdpdGggYGxhc3RfY29sKClgOgoKYGBge3J9CnNlbGVjdChwb2xpY2UsIGxhc3RfY29sKCkpCmBgYAoKTGFzdCA0IGNvbHVtbnMgKHRoZSBpbnB1dCB0byB0aGUgZnVuY3Rpb24gaXMgdGhlIG9mZnNldCAjIG9mIGNvbHVtbnMgZnJvbSB0aGUgcmlnaHQgZWRnZSk6CgpgYGB7cn0Kc2VsZWN0KHBvbGljZSwgbGFzdF9jb2woMDozKSkKYGBgCgpXZSBjYW4gYWxzbyBzZWxlY3QgYnkgbWF0Y2hpbmcgcGF0dGVybnMgaW4gdGhlIG5hbWVzIG9mIHRoZSBjb2x1bW5zOgoKYGBge3J9CnNlbGVjdChwb2xpY2UsIHN0YXJ0c193aXRoKCJyYXciKSkKYGBgCgpgYGB7cn0Kc2VsZWN0KHBvbGljZSwgZW5kc193aXRoKCJpc3N1ZWQiKSkKYGBgCgpgYGB7cn0Kc2VsZWN0KHBvbGljZSwgY29udGFpbnMoInZlaGljbGUiKSkKYGBgCgpXZSBjYW4gYWxzbyBwdXQgYSBgLWAgaW4gZnJvbnQgb2YgdGhlc2UgaGVscGVyIGZ1bmN0aW9ucyB0byBleGNsdWRlIGNvbHVtbnM6CgpgYGB7cn0Kc2VsZWN0KHBvbGljZSwgLWNvbnRhaW5zKCJzdWJqZWN0IikpCmBgYAoKCkFuZCB0aGVyZSBhcmUgZXZlbiBtb3JlIFtoZWxwZXIgZnVuY3Rpb25zXShodHRwczovL3RpZHlzZWxlY3Quci1saWIub3JnL3JlZmVyZW5jZS9zZWxlY3RfaGVscGVycy5odG1sKS4gIAoKIyMjIEVYRVJDSVNFCgpVc2UgYHNlbGVjdCgpYCB0byBnZXQgYSBjb3B5IG9mIGBwb2xpY2VgIHdpdGhvdXQgdGhlIGNvbHVtbnMgdGhhdCBzdGFydCB3aXRoICJyYXciLgoKYGBge3J9CgpgYGAKCkhpbnQ6IElmIHlvdSBtZXNzIHVwIHlvdXIgYHBvbGljZWAgZGF0YXNldCwgcmUtcnVuIHRoZSBjZWxsIG5lYXIgdGhlIHRvcCBvZiB0aGUgZmlsZSB1bmRlciB0aGUgRGF0YSBoZWFkZXIgYW5kIHJlYWQgdGhlIGRhdGEgaW4gYWdhaW4gZnJlc2guCgojIyMgUmVuYW1pbmcKCldlIGNhbiBhbHNvIHJlbmFtZSBjb2x1bW5zIHdoaWxlIHVzaW5nIGBzZWxlY3QoKWAuICBUaGUgc3ludGF4IGlzIGBuZXdfbmFtZSA9IG9sZF9uYW1lYC4KCmBgYHtyfQpzZWxlY3QocG9saWNlLCByYXdfaWQ9cmF3X3Jvd19udW1iZXIsIGRhdGUsIHRpbWUpCmBgYAoKCm9yIHdlIGNhbiB1c2UgYHJlbmFtZSgpYCB0byBvbmx5IHJlbmFtZSwgd2l0aG91dCBhZmZlY3Rpbmcgd2hpY2ggY29sdW1ucyBhcmUgaW5jbHVkZWQ6CgpgYGB7cn0KcmVuYW1lKHBvbGljZSwgcmF3X2lkPXJhd19yb3dfbnVtYmVyKQpgYGAKClRoaXMgZG9lc24ndCBjaGFuZ2UgcG9saWNlIGJlY2F1c2Ugd2UgZGlkbid0IHNhdmUgdGhlIHJlc3VsdC4gIFNvIGZhciwgd2UndmUganVzdCBiZWVuIHByaW50aW5nIHRoZSBjb3B5IG9mIHRoZSBkYXRhIGZyYW1lIHRoYXQgaXMgcmV0dXJuZWQgYnkgdGhlIGZ1bmN0aW9uLiAgSWYgd2Ugd2FudCB0byBjaGFuZ2Ugb3VyIGRhdGEgZnJhbWUsIHdlJ2QgbmVlZCB0byBzYXZlIHRoZSByZXN1bHQgYmFjayB0byB0aGUgYHBvbGljZWAgb2JqZWN0LgoKYGBge3J9CnBvbGljZSA8LSByZW5hbWUocG9saWNlLCByYXdfaWQ9cmF3X3Jvd19udW1iZXIpCmBgYAoKCiMjIEZpbHRlcjogQ2hvb3NlIFJvd3MKClRoZSBgZmlsdGVyKClgIGZ1bmN0aW9uIGxldHMgdXMgY2hvb3NlIHdoaWNoIHJvd3Mgb2YgZGF0YSB0byBrZWVwLiAgVGFrZXMgdGhlIGRhdGEgZnJhbWUgbmFtZSBmaXJzdCwgYW5kIHRoZW4gYW55IGV4cHJlc3Npb24gdGhhdCB3aWxsIHJldHVybiBhIHZlY3RvciBvZiBgVFJVRWAgYW5kIGBGQUxTRWAgdmFsdWVzIHRoYXQgaXMgdGhlIHNhbWUgbGVuZ3RoIGFzIHRoZSBudW1iZXIgb2Ygcm93cyBpbiB0aGUgZGF0YSBmcmFtZS4KCmBgYHtyfQpmaWx0ZXIocG9saWNlLCBkYXRlID09ICIyMDE3LTAxLTAyIikKYGBgCgpXZSBjYW4gZG8gY29tcGxleCBjb25kaXRpb25zIGFzIHdlIGNvdWxkIGRvIHdpdGggYFtdYAoKYGBge3J9CmZpbHRlcihwb2xpY2UsIHN1YmplY3RfcmFjZSA9PSAiaGlzcGFuaWMiICYgc3ViamVjdF9zZXggPT0gImZlbWFsZSIpCmBgYAoKSWYgd2UgaW5jbHVkZSBtdWx0aXBsZSBjb21tYS1zZXBhcmF0ZWQgY29uZGl0aW9ucywgdGhleSBhcmUgam9pbmVkIHdpdGggYCZgIGFuZC4gIFNvIHRoaXMgZm9sbG93aW5nIGlzIGVxdWl2YWxlbnQgdG8gdGhlIGFib3ZlLgoKYGBge3J9CmZpbHRlcihwb2xpY2UsIHN1YmplY3RfcmFjZSA9PSAiaGlzcGFuaWMiLCBzdWJqZWN0X3NleCA9PSAiZmVtYWxlIikKYGBgCgoKIyMjIEVYRVJDSVNFCgoxLiBGaWx0ZXIgYHBvbGljZWAgdG8gY2hvb3NlIHRoZSByb3dzIHdoZXJlIGxvY2F0aW9uIGlzIDYwMjAxIG9yIDYwMjAyCjIuIEZpbHRlciBgcG9saWNlYCB0byBjaG9vc2UgdGhlIHJvd3Mgd2hlcmUgbG9jYXRpb24gaXMgNjAyMDEgb3IgNjAyMDIgYW5kIHN1YmplY3Rfc2V4IGlzICJtYWxlIgoKSGludHM6CgoqIFRoZSAib3IiIG9wZXJhdG9yIGlzIGB8YDsgdGhlICJhbmQiIG9wZXJhdG9yIGlzIGAmYAoKYGBge3J9CgpgYGAKCgojIyBQaXBlOiBDaGFpbmluZyBDb21tYW5kcyBUb2dldGhlcgoKU28sIHdlIGNhbiBjaG9vc2Ugcm93cyBhbmQgY2hvb3NlIGNvbHVtbnMgc2VwYXJhdGVseTsgaG93IGRvIHdlIGNvbWJpbmUgdGhlc2Ugb3BlcmF0aW9ucz8gIGBkcGx5cmAsIGFuZCBvdGhlciB0aWR5dmVyc2UsIGNvbW1hbmRzIGNhbiBiZSBzdHJ1bmcgdG9nZXRoZXIgaXMgYSBzZXJpZXMgd2l0aCBhIGAlPiVgIChzYXkvcmVhZDogcGlwZSkgb3BlcmF0b3IuICBJZiB5b3UgYXJlIGZhbWlsaWFyIHdpdGggd29ya2luZyBpbiBhIHRlcm1pbmFsL2F0IHRoZSBjb21tYW5kIGxpbmUsIGl0IHdvcmtzIGxpa2UgYSBiYXNoIHBpcGUgY2hhcmFjdGVyIGB8YC4gIEl0IHRha2VzIHRoZSBvdXRwdXQgb2YgdGhlIGNvbW1hbmQgb24gdGhlIGxlZnQgYW5kIG1ha2VzIHRoYXQgdGhlIGZpcnN0IGlucHV0IHRvIHRoZSBjb21tYW5kIG9uIHRoZSByaWdodC4gCgpUaGlzIHdvcmtzIGJlY2F1c2UgdGhlIGZ1bmN0aW9ucyBhbGwgdGFrZSBhIGRhdGEgZnJhbWUgYXMgdGhlIGZpcnN0IGlucHV0LCBhbmQgdGhleSByZXR1cm4gYSBkYXRhIGZyYW1lIGFzIHRoZSBvdXRwdXQuICAKCldlIGNhbiByZXdyaXRlIAoKYGBge3J9CnNlbGVjdChwb2xpY2UsIGRhdGUsIHRpbWUpCmBgYAoKYXMKCmBgYHtyfQpwb2xpY2UgJT4lIHNlbGVjdChkYXRlLCB0aW1lKQpgYGAKCmFuZCB5b3UnbGwgb2Z0ZW4gc2VlIGNvZGUgZm9ybWF0dGVkLCBzbyBgJT4lYCBpcyBhdCB0aGUgZW5kIG9mIGVhY2ggbGluZToKCmBgYHtyfQpwb2xpY2UgJT4lCiAgc2VsZWN0KGRhdGUsIHRpbWUpCmBgYAoKVGhlIHBpcGUgY29tZXMgZnJvbSBhIHBhY2thZ2UgY2FsbGVkIGBtYWdyaXR0cmAsIHdoaWNoIGhhcyBhZGRpdGlvbmFsIHNwZWNpYWwgb3BlcmF0b3JzIGluIGl0IHRoYXQgeW91IGNhbiB1c2UuICBUaGUga2V5Ym9hcmQgc2hvcnRjdXQgZm9yIGAlPiVgIGlzIGNvbW1hbmQtc2hpZnQtTSAoTWFjKSBvciBjb250cm9sLXNoaWZ0LU0gKFdpbmRvd3MpLgoKV2UgY2FuIHVzZSB0aGUgcGlwZSB0byBzdHJpbmcgdG9nZXRoZXIgbXVsdGlwbGUgY29tbWFuZHMgb3BlcmF0aW5nIG9uIHRoZSBzYW1lIGRhdGEgZnJhbWU6CgpgYGB7cn0KcG9saWNlICU+JQogIHNlbGVjdChzdWJqZWN0X3JhY2UsIHN1YmplY3Rfc2V4KSAlPiUKICBmaWx0ZXIoc3ViamVjdF9yYWNlID09ICJ3aGl0ZSIpCmBgYAoKV2Ugd291bGQgcmVhZCB0aGUgYCU+JWAgaW4gdGhlIGNvbW1hbmQgYWJvdmUgYXMgInRoZW4iIGlmIHJlYWRpbmcgdGhlIGNvZGUgb3V0bG91ZDogZnJvbSBwb2xpY2UsIHNlbGVjdCBzdWJqZWN0X3JhY2UgYW5kIHN1YmplY3Rfc2V4LCB0aGVuIGZpbHRlciB3aGVyZSBzdWJqZWN0X3JhY2UgaXMgd2hpdGUuCgpPcmRlciBkb2VzIG1hdHRlciwgYXMgdGhlIGNvbW1hbmRzIGFyZSBleGVjdXRlZCBpbiBvcmRlci4gIFNvIHRoaXMgd291bGQgZ2l2ZSB1cyBhbiBlcnJvcjoKCmBgYHtyfQpwb2xpY2UgJT4lCiAgc2VsZWN0KHN1YmplY3Rfc2V4LCBvdXRjb21lKSAlPiUKICBmaWx0ZXIoc3ViamVjdF9yYWNlID09ICJ3aGl0ZSIpCmBgYAoKQmVjYXVzZSBgc3ViamVjdF9yYWNlYCBpcyBubyBsb25nZXIgaW4gdGhlIGRhdGEgZnJhbWUgb25jZSB3ZSB0cnkgdG8gZmlsdGVyIG9uIGl0LiAgV2UnZCBoYXZlIHRvIHJldmVyc2UgdGhlIG9yZGVyOgoKYGBge3J9CnBvbGljZSAlPiUKICBmaWx0ZXIoc3ViamVjdF9yYWNlID09ICJ3aGl0ZSIpICU+JQogIHNlbGVjdChzdWJqZWN0X3NleCwgb3V0Y29tZSkKYGBgCgpZb3UgY2FuIHVzZSB0aGUgcGlwZSBvcGVyYXRvciB0byBzdHJpbmcgdG9nZXRoZXIgY29tbWFuZHMgb3V0c2lkZSBvZiB0aGUgdGlkeXZlcnNlIGFzIHdlbGw6CgpgYGB7cn0KIyBzb3J0KHRhYmxlKHBvbGljZSRzdWJqZWN0X3JhY2UpKSBiZWNvbWVzOiAKdGFibGUocG9saWNlJHN1YmplY3RfcmFjZSkgJT4lIHNvcnQoKQpgYGAKCgojIyMgRVhFUkNJU0UKClNlbGVjdCB0aGUgZGF0ZSwgdGltZSwgYW5kIG91dGNvbWUgKGNvbHVtbnMpIG9mIHN0b3BzIHRoYXQgb2NjdXIgaW4gYmVhdCA3MSAocm93cykuICBIaW50OiByZW1lbWJlciB0aGF0IGEgY29sdW1uIG5lZWRzIHRvIHN0aWxsIGJlIGluIHRoZSBkYXRhIGZyYW1lIGlmIHlvdSdyZSBnb2luZyB0byB1c2UgdGhlIGNvbHVtbiB0byBmaWx0ZXIuCgpgYGB7cn0KCmBgYAoKCgojIyBNdXRhdGU6IENoYW5nZSBvciBDcmVhdGUgQ29sdW1ucwoKYG11dGF0ZSgpYCBpcyB1c2VkIHRvIGJvdGggY2hhbmdlIHRoZSB2YWx1ZXMgb2YgYW4gZXhpc3RpbmcgY29sdW1uIGFuZCBtYWtlIGEgbmV3IGNvbHVtbi4gIAoKYGBge3J9Cm11dGF0ZShwb2xpY2UsIHZlaGljbGVfYWdlID0gMjAxNyAtIHZlaGljbGVfeWVhcikgJT4lCiAgc2VsZWN0KHN0YXJ0c193aXRoKCJ2ZWhpY2xlIikpCmBgYAoKV2l0aGluIGEgY2FsbCB0byBtdXRhdGUsIHdlIGNhbiByZWZlciB0byB2YXJpYWJsZXMgd2UgbWFkZSBvciBjaGFuZ2VkIGVhcmxpZXIgaW4gdGhlIHNhbWUgY2FsbCBhcyB3ZWxsLiAgSGVyZSwgd2UgY3JlYXRlIHZlaGljbGVfYWdlLCBhbmQgdGhlbiB1c2UgaXQgdG8gY3JlYXRlIHZlaGljbGVfYWdlX25vcm06CgpgYGB7cn0KcG9saWNlICU+JSAKICBtdXRhdGUodmVoaWNsZV9hZ2UgPSAyMDE3IC0gdmVoaWNsZV95ZWFyLCAKICAgICAgICAgdmVoaWNsZV9hZ2Vfbm9ybSA9IGlmZWxzZSh2ZWhpY2xlX2FnZSA8IDAsIDAsIHZlaGljbGVfYWdlKSkgJT4lCiAgc2VsZWN0KHN0YXJ0c193aXRoKCJ2ZWhpY2xlIikpICU+JQogIGZpbHRlcih2ZWhpY2xlX2FnZSA8IDApCmBgYAoKU2lkZSBub3RlOiB0aGVyZSBpcyBhIHRpZHl2ZXJzZSB2ZXJzaW9uIG9mIGBpZmVsc2UoKWAgY2FsbGVkIGBpZl9lbHNlKClgIHRoYXQgd29ya3MgZ2VuZXJhbGx5IHRoZSBzYW1lIGV4Y2VwdCBpdCBpcyBzdHJpY3RlciBhYm91dCBjaGVja2luZyBkYXRhIHR5cGVzLgoKYG11dGF0ZSgpYCBjYW4gYWxzbyBjaGFuZ2UgYW4gZXhpc3RpbmcgY29sdW1uLiAgVGhlIGxvY2F0aW9uIGNvbHVtbiBpbiB0aGUgZGF0YSBjb250YWlucyB6aXAgY29kZXMsIHRoYXQgd2VyZSByZWFkIGluIGFzIG51bWVyaWMgdmFsdWVzLiAgVGhpcyBtZWFucyB0aGUgbGVhZGluZyB6ZXJvIG9uIHNvbWUgemlwIGNvZGVzIGhhcyBiZWVuIGxvc3QuICBDb252ZXJ0IGxvY2F0aW9uIHRvIGNoYXJhY3RlciBkYXRhLCBhbmQgYWRkIGJhY2sgaW4gdGhlIGxlYWRpbmcgMCBpZiBpdCBzaG91bGQgYmUgdGhlcmUuCgpIZXJlIEknbGwgY2hhbmdlIHRoZSBsb2NhdGlvbiBjb2x1bW4gdHdpY2UgaW4gdGhlIHNhbWUgY2FsbCB3aXRoIHR3byBkaWZmZXJlbnQgdHJhbnNmb3JtYXRpb25zOgoKYGBge3J9CnBvbGljZSAlPiUKICBtdXRhdGUobG9jYXRpb24gPSBhcy5jaGFyYWN0ZXIobG9jYXRpb24pLCAgIyBmaXJzdCBjb252ZXJ0IHRvIGNoYXJhY3RlciwgdGhlbiByZWNvZGUgYmVsb3cKICAgICAgICAgbG9jYXRpb24gPSBpZmVsc2UobmNoYXIobG9jYXRpb24pID09IDQsICAjIGlmZWxzZSB0ZXN0ICh2ZWN0b3Igb2YgVFJVRSBhbmQgRkFMU0UpCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhc3RlMCgiMCIsIGxvY2F0aW9uKSwgIyB2YWx1ZSBpZiBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvY2F0aW9uKSkgJT4lICAjIHZhbHVlIGlmIEZBTFNFCiAgc2VsZWN0KGxvY2F0aW9uKSAlPiUgICMgc2VsZWN0aW5nIGp1c3QgdGhlIGNvbHVtbiB3ZSBtdXRhdGVkIHRvIGxvb2sgYXQKICBmaWx0ZXIoc3RhcnRzV2l0aChsb2NhdGlvbiwgIjAiKSkgICMgc2VsZWN0aW5nIGEgZmV3IHJvd3MgdG8gbG9vayBhdCB0aGUgY2hhbmdlCmBgYAoKUmVtZW1iZXIgdGhhdCB3aGVuIHVzaW5nIGBtdXRhdGUoKWAsIHlvdSdyZSBvcGVyYXRpbmcgb24gdGhlIGVudGlyZSBjb2x1bW4gYXQgb25jZSwgc28geW91IGNhbid0IHNlbGVjdCBqdXN0IGEgc3Vic2V0IG9mIHRoZSB2ZWN0b3IgYXMgeW91IHdvdWxkIHdpdGggYFtdYC4gIFRoaXMgbWVhbnMgbW9yZSBmcmVxdWVudGx5IHVzaW5nIGZ1bmN0aW9ucyBsaWtlIGBpZmVsc2UoKWAgb3IgaGVscGVyIGZ1bmN0aW9ucyBzdWNoIGFzIGBuYV9pZigpYCwgYHJlcGxhY2VfbmEoKWAsIG9yIGByZWNvZGUoKWAuICAKCmBgYHtyfQptdXRhdGUocG9saWNlLCB2ZWhpY2xlX21ha2UgPSBuYV9pZih2ZWhpY2xlX21ha2UsICJVTksiKSkKYGBgCgoKIyMjIEVYRVJDSVNFCgpJZiBiZWF0IGlzICIvIiBvciAiQ0hJQ0FHTyIsIHNldCBpdCB0byBgTkFgIGluc3RlYWQgdXNpbmcgYG11dGF0ZSgpYC4gIAoKSGludDogaWYgeW91IHVzZSBgbmFfaWYoKWAsIGl0IG9ubHkgY2FuIGNoZWNrIGFuZCByZXBsYWNlIG9uZSB2YWx1ZSBhdCBhIHRpbWUsIHNvIHlvdSdkIG5lZWQgdG8gdXNlIGl0IHR3aWNlICh3aGljaCBpcyBPSyEpLiAgV2l0aCBgaWZlbHNlKClgIHlvdSBjYW4gd3JpdGUgYW4gZXhwcmVzc2lvbiBsaWtlIChgYmVhdCA9PSAnLycgfCBiZWF0ID09ICdDSElDQUdPJ2ApIG9yIChgYmVhdCAlaW4lIGMoJy8nLCAnQ0hJQ0FHTycpYCkgZm9yIHRoZSBmaXJzdCBpbnB1dCAodGhlIFRSVUUvRkFMU0UgdGVzdCkuICAKCmBgYHtyfQoKYGBgCgojIFJlY2FwCgpXZSBsZWFybmVkIHRoZSBkcGx5ciBlcXVpdmFsZW50cyBvZiBpbmRleGluZyBhbmQgc3Vic2V0dGluZyBhIGRhdGEgZnJhbWUsIG9mIGNyZWF0aW5nIG5ldyB2YXJpYWJsZXMgaW4gb3VyIGRhdGEgZnJhbWUsIGFuZCBvZiByZWNvZGluZyB2YXJpYWJsZXMgaW4gb3VyIGRhdGEgZnJhbWUuICBXZSBhbHNvIGxlYXJuZWQgYWJvdXQgdGhlIHBpcGUgYCU+JWAgb3BlcmF0b3IsIGFuZCB3aGF0IHRpYmJsZXMgYXJlLgoKTmV4dCB3ZWVrOiB0aGUgdGhyZWUgb3RoZXIgY29tbW9uIGRwbHlyICJ2ZXJiIiBmdW5jdGlvbnMgZm9yIHdvcmtpbmcgd2l0aCBkYXRhIGZyYW1lczogZ3JvdXBfYnksIHN1bW1hcml6ZSwgYW5kIGFycmFuZ2UuICAK